/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "tests/common.h"
#include "bytecode_optimizer/reg_encoder.h"

namespace ark::bytecodeopt::test {

// NOLINTBEGIN(readability-magic-numbers)

TEST_F(CommonTest, RegEncoderEcmaSuperCallIntrinsic)
{
    auto graph = CreateEmptyGraph();
    graph->SetDynamicMethod();
    ArenaVector<bool> regMask(254, false, graph->GetLocalAllocator()->Adapter());
    graph->InitUsedRegs<compiler::DataType::INT64>(&regMask);
    GRAPH(graph)
    {
        PARAMETER(0, 0).any().DstReg(compiler::ACC_REG_ID - 3);
        PARAMETER(1, 1).any().DstReg(compiler::ACC_REG_ID - 2);
        PARAMETER(2, 2).any().DstReg(compiler::ACC_REG_ID - 1);
        BASIC_BLOCK(2, -1)
        {
            INST(3, Opcode::SaveState).NoVregs();
            INST(4, Opcode::Intrinsic)
                .any()
                .IntrinsicId(compiler::RuntimeInterface::IntrinsicId::INTRINSIC_LDTRUE)
                .Inputs({{compiler::DataType::NO_TYPE, 3}})
                .DstReg(compiler::ACC_REG_ID);
            INST(5, Opcode::SaveState).NoVregs();
            INST(6, Opcode::Intrinsic)
                .any()
                .IntrinsicId(compiler::RuntimeInterface::IntrinsicId::INTRINSIC_SUPER_CALL)
                .Inputs({{compiler::DataType::ANY, 0},
                         {compiler::DataType::ANY, 1},
                         {compiler::DataType::ANY, 2},
                         {compiler::DataType::ANY, 4},
                         {compiler::DataType::NO_TYPE, 5}})
                .DstReg(compiler::ACC_REG_ID);
            INST(7, Opcode::Return).any().Inputs(6);
        }
    }

    graph->InitDefaultLocations();

    INS(4).SetFlag(compiler::inst_flags::ACC_WRITE);

    INS(6).SetSrcReg(3, compiler::ACC_REG_ID);
    INS(6).SetFlag(compiler::inst_flags::ACC_READ);

    EXPECT_TRUE(graph->RunPass<RegEncoder>());

    auto expected = CreateEmptyGraph();
    expected->SetDynamicMethod();
    GRAPH(expected)
    {
        PARAMETER(0, 0).any();
        PARAMETER(1, 1).any();
        PARAMETER(2, 2).any();
        BASIC_BLOCK(2, -1)
        {
            INST(3, Opcode::SaveState).NoVregs();
            INST(4, Opcode::Intrinsic)
                .any()
                .IntrinsicId(compiler::RuntimeInterface::IntrinsicId::INTRINSIC_LDTRUE)
                .Inputs({{compiler::DataType::NO_TYPE, 3}})
                .DstReg(compiler::ACC_REG_ID);
            INST(5, Opcode::SaveState).NoVregs();
            INST(8, Opcode::SpillFill);
            INST(6, Opcode::Intrinsic)
                .any()
                .IntrinsicId(compiler::RuntimeInterface::IntrinsicId::INTRINSIC_SUPER_CALL)
                .Inputs({{compiler::DataType::ANY, 0},
                         {compiler::DataType::ANY, 1},
                         {compiler::DataType::ANY, 2},
                         {compiler::DataType::ANY, 4},
                         {compiler::DataType::NO_TYPE, 5}})
                .DstReg(compiler::ACC_REG_ID);
            INST(7, Opcode::Return).any().Inputs(6);
        }
    }

    EXPECT_TRUE(GraphComparator().Compare(graph, expected));

    auto expectedData = std::vector {std::make_tuple(compiler::LocationType::REGISTER, compiler::LocationType::REGISTER,
                                                     compiler::INVALID_REG_ID, 0U),
                                     std::make_tuple(compiler::LocationType::REGISTER, compiler::LocationType::REGISTER,
                                                     compiler::INVALID_REG_ID, 1U),
                                     std::make_tuple(compiler::LocationType::REGISTER, compiler::LocationType::REGISTER,
                                                     compiler::INVALID_REG_ID, 2U)};

    size_t spillFills = 0;
    for (auto block : graph->GetVectorBlocks()) {
        if (block == nullptr) {
            continue;
        }
        for (auto inst : block->AllInsts()) {
            if (inst->GetOpcode() != Opcode::SpillFill) {
                continue;
            }

            auto sf = inst->CastToSpillFill();
            auto &data = sf->GetSpillFills();
            ASSERT_EQ(data.size(), expectedData.size());

            for (size_t i = 0; i < data.size(); i++) {
                auto &sfData = data[i];
                auto &[src_type, dst_type, src, dst] = expectedData[i];
                EXPECT_EQ(sfData.SrcType(), src_type);
                EXPECT_EQ(sfData.DstType(), dst_type);
                EXPECT_EQ(sfData.SrcValue(), src);
                EXPECT_EQ(sfData.DstValue(), dst);
            }

            spillFills++;
        }
    }
    EXPECT_EQ(spillFills, 1);
}

// NOLINTEND(readability-magic-numbers)

}  // namespace ark::bytecodeopt::test
